#include "idt.h"
#include "string.h"
#include "debug.h"
#include "vmm.h"
#include "pmm.h"

// 内核页目录区域
pgd_t pgd_kernel[PGD_SIZE] __attribute__((aligned(PAGE_SIZE)));
// 内核页表区域
static pte_t pte_kernel[PTE_COUNT][PTE_SIZE] __attribute__((aligned(PAGE_SIZE)));

void init_vmm()
{
    // 0xC0000000 这个地址在目录的索引
    uint32_t kernel_pte_first_idx = PGD_INDEX(PAGE_OFFSET);
    uint32_t i;
    uint32_t j = 0;
    for (i = kernel_pte_first_idx; i < PTE_COUNT + kernel_pte_first_idx; i++, j++)
    {
        pgd_kernel[i] = ((uint32_t)pte_kernel[j] - PAGE_OFFSET) | PAGE_PRESENT | PAGE_WRITE;
    }
    uint32_t *pte = (uint32_t *)pte_kernel;
    for (i = 1; i < PTE_COUNT * PTE_SIZE; i++)
    {
        pte[i] = (i << 12) | PAGE_PRESENT | PAGE_WRITE;
    }
    uint32_t pgd_kern_phy_addr = (uint32_t)pgd_kernel - PAGE_OFFSET;
    // 注册页错误中断处理函数
    register_interrupt_handler(14, &page_fault);
    // 开启pgd
    switch_pgd(pgd_kern_phy_addr);
}
void switch_pgd(uint32_t pd)
{
    asm volatile("mov %0,%%cr3" ::"r"(pd));
}
/**
 * @brief 先找页目录，再找页表，如果没有页表 就去创建并更新cpu缓存
 *
 * @param page_now
 * @param va
 * @param pa
 * @param flags
 */
void map(pgd_t *pgd_now, uint32_t va, uint32_t pa, uint32_t flags)
{
    // 获取页目录 page_dirctory
    uint32_t pgd_idx = PGD_INDEX(va);
    // 获取页表
    uint32_t pte_idx = PTE_INDEX(va);
    // 获取真实页表 page_table_entry
    pte_t *pte = (pte_t *)(pgd_now[pgd_idx] & PAGE_MASK);
    // 真实页表不存在，则去创建
    if (!pte)
    {
        pte = (pte_t *)pmm_alloc_page();
        pgd_now[pgd_idx] = (uint32_t)pte | PAGE_PRESENT | PAGE_WRITE;
        pte = (pte_t *)((uint32_t)pte + PAGE_OFFSET);
        bzero(pte, PAGE_SIZE);
    }
    else
    {
        pte = (pte_t *)((uint32_t)pte + PAGE_OFFSET);
    }
    pte[pte_idx] = (pa & PAGE_MASK) | flags;
    // 通知CPU更新页表缓存
    asm volatile("invlpg (%0)" ::"a"(va));
}
void unmap(pgd_t *pgd_now, uint32_t va)
{
    // 获取页目录 page_dirctory
    uint32_t pgd_idx = PGD_INDEX(va);
    // 获取页表索引
    uint32_t pte_idx = PTE_INDEX(va);
    // 获取真实页表 page_table_entry
    pte_t *pte = (pte_t *)(pgd_now[pgd_idx] & PAGE_MASK);
    if (!pte)
    {
        return;
    }
    // 转换到内核线性地址
    pte = (pte_t *)((uint32_t)pte + PAGE_OFFSET);

    pte[pte_idx] = 0;
    // 通知CPU更新页表缓存
    asm volatile("invlpg (%0)" ::"a"(va));
}
uint32_t get_mapping(pgd_t *pgd_now, uint32_t va, uint32_t *pa)
{
    uint32_t pgd_idx = PGD_INDEX(va);
    uint32_t pte_idx = PTE_INDEX(va);
    pte_t *pte = (pte_t *)(pgd_now[pgd_idx] & PAGE_MASK);
    if (!pte)
    {
        return 0;
    }
    // 转化到内核线性地址
    pte = (pte_t *)((uint32_t)pte + PAGE_OFFSET);

    if (pte[pte_idx] != 0 && pa)
    {
        *pa = pte[pte_idx] & PAGE_MASK;
        return 1;
    }
    return 1;
}

void page_fault(pt_regs *regs)
{
    uint32_t cr2;
    // 获取cr2寄存器的值
    asm volatile("mov %%cr2, %0"
                 : "=r"(cr2));

    printk("Page fault at 0x%x, virtual faulting address 0x%x\n", regs->eip, cr2);
    printk("Error code: %x\n", regs->err_code);
    // bit 0 为 0 指页面不存在内存里
    if (!(regs->err_code & 0x1))
    {
        printk_color(rc_black, rc_red, "Because the page wasn't present.\n");
    }
    // bit 1 为 0 表示读错误，为 1 为写错误
    if (regs->err_code & 0x2)
    {
        printk_color(rc_black, rc_red, "Write error.\n");
    }
    else
    {
        printk_color(rc_black, rc_red, "Read error.\n");
    }
    // bit 2 为 1 表示在用户模式打断的，为 0 是在内核模式打断的
    if (regs->err_code & 0x4)
    {
        printk_color(rc_black, rc_red, "In user mode.\n");
    }
    else
    {
        printk_color(rc_black, rc_red, "In kernel mode.\n");
    }
    // bit 3 为 1 表示错误是由保留位覆盖造成的
    if (regs->err_code & 0x8)
    {
        printk_color(rc_black, rc_red, "Reserved bits being overwritten.\n");
    }
    // bit 4 为 1 表示错误发生在取指令的时候
    if (regs->err_code & 0x10)
    {
        printk_color(rc_black, rc_red, "The fault occurred during an instruction fetch.\n");
    }

    while (1)
        ;
}